<script lang="ts" setup>
import type { JSX } from 'vue/jsx-runtime';

import type { PropType, VNode } from 'vue';

import type { Recordable } from '@vben/types';

import { computed, defineProps } from 'vue';

import { get, getNestedValue, isFunction } from '@vben/utils';

import { Badge, Col, Flex, Row, Typography } from 'ant-design-vue';

export interface DescriptionItemSchema {
  // 对应 data 中的字段名
  field: string;
  // 内容的描述
  label: JSX.Element | string | VNode;
  // col的span 24为一行
  span?: number;
  // 是否显示
  show?: (...arg: any) => boolean;
  // 自定义需要展示的内容
  render?: (
    val: any,
    data?: Recordable<any>,
  ) => Element | JSX.Element | number | string | undefined | VNode;
}
const props = defineProps({
  labelWidth: { default: 60, type: Number },
  schema: {
    default: () => [],
    type: Array as PropType<DescriptionItemSchema[]>,
  },
  info: {
    default: () => {
      return {};
    },
    type: Object,
  },
  changeList: {
    default: () => [],
    type: Array,
  },
  changeType: { default: 'update', type: String },
  highlightMode: { default: 'NORMAL', type: String },
});

const useSchema = computed(() => {
  return props.schema
    .filter((item) => {
      // 如果存在 show 函数且返回 false，则过滤掉该项
      if (item.show && isFunction(item.show)) {
        return item.show(props.info);
      }
      return true;
    })
    .map((item) => {
      const oldItem = props.changeList.find((changeItem) => {
        return item.field.includes('.')
          ? item.field.split('.')[0] === changeItem.field
          : item.field === changeItem.field;
      });
      const newVal = item.field.includes('.')
        ? (getNestedValue(props.info, item.field) ??
          get(props.info, item.field))
        : get(props.info, item.field);
      if (newVal) {
        item.newValue = newVal;
        item.oldValue = newVal;
      }
      if (oldItem) {
        item.oldValue = oldItem?.beforeValue || '（空）';
        item.newValue = oldItem?.afterValue || '（空）';
      }

      return item;
    });
});
function getContent(field: string, render: any) {
  const _data = props?.info;
  if (!_data) {
    return null;
  }
  const getField = field.includes('.')
    ? (getNestedValue(_data, field) ?? get(_data, field))
    : get(_data, field);
  return isFunction(render) ? render(getField, _data) : (getField ?? '');
}
function getContentChange(val: any, field: string, render: any) {
  let getField = '';
  if (field.includes('.')) {
    const fieldArr = field.split('.');
    const newField = fieldArr.slice(1).join('.');
    getField = newField.includes('.')
      ? (getNestedValue(val, newField) ?? get(val, newField))
      : get(val, newField);
  } else {
    getField = val;
  }
  return isFunction(render) ? render(getField, {}) : (getField ?? '');
}
function isVNode(obj: any): boolean {
  return obj && typeof obj === 'object' && obj.__v_isVNode;
}
</script>
<template>
  <Row :gutter="[16, 16]">
    <Col :span="item.span ?? 8" v-for="item in useSchema" :key="item.field">
      <div style="padding: 0 10px">
        <Flex>
          <Typography.Text type="secondary">
            <span
              :style="
                labelWidth > 0
                  ? `display: inline-block; width: ${labelWidth}px`
                  : ''
              "
              >{{ item.label }}:</span
            >
          </Typography.Text>
          <Typography.Text>
            <Badge
              :dot="
                changeType === 'add' ||
                changeType === 'delete' ||
                item.newValue !== item.oldValue
              "
            >
              <component
                :is="getContentChange(item.oldValue, item.field, item.render)"
                v-if="
                  item.render &&
                  isVNode(
                    getContentChange(item.oldValue, item.field, item.render),
                  )
                "
              />
              <span v-else>{{
                getContentChange(item.oldValue, item.field, item.render)
              }}</span>
            </Badge>
          </Typography.Text>
        </Flex>
      </div>
      <div
        v-if="
          highlightMode !== 'NORMAL' &&
          changeType === 'update' &&
          item.newValue !== item.oldValue
        "
        style="
          padding: 4px 10px;
          margin-top: 4px;
          color: hsl(var(--accent-foreground));
          background-color: hsl(var(--accent));
        "
      >
        <Flex>
          <Typography.Text type="secondary">
            <span
              :style="
                labelWidth > 0
                  ? `display: inline-block; width: ${labelWidth}px`
                  : ''
              "
              >请求变更为:</span
            >
          </Typography.Text>
          <Typography.Text type="danger">
            <component
              :is="getContentChange(item.newValue, item.field, item.render)"
              v-bind="{ 'color-type': 'danger' }"
              v-if="
                item.render &&
                isVNode(
                  getContentChange(item.newValue, item.field, item.render),
                )
              "
            />
            <span v-else>{{
              getContentChange(item.newValue, item.field, item.render)
            }}</span>
          </Typography.Text>
        </Flex>
      </div>
    </Col>
  </Row>
</template>
